import { createClient } from '@supabase/supabase-js' import { NextResponse } from 'next/server' import type { FileSystemItem, CreateFileSystemItemRequest, UpdateFileSystemItemRequest } from '@/lib/types/database' // Create server-side Supabase client with user session function createServerSupabaseClient(request: Request) { // Get the authorization header from the request const authHeader = request.headers.get('authorization') return createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { auth: { autoRefreshToken: false, persistSession: false }, global: { headers: authHeader ? { Authorization: authHeader } : {} } } ) } // Helper function to transform flat array into hierarchical tree function buildFileTree(items: FileSystemItem[]): (FileSystemItem & { children?: FileSystemItem[] })[] { const itemMap = new Map() const roots: (FileSystemItem & { children: FileSystemItem[] })[] = [] // Create map of all items with children arrays items.forEach(item => { itemMap.set(item.id, { ...item, children: [] }) }) // Build tree structure items.forEach(item => { const itemWithChildren = itemMap.get(item.id) if (!itemWithChildren) return if (item.parent_id) { const parent = itemMap.get(item.parent_id) if (parent) { parent.children.push(itemWithChildren) } } else { roots.push(itemWithChildren) } }) // Sort children recursively const sortItems = (items: (FileSystemItem & { children: FileSystemItem[] })[]) => { items.sort((a, b) => { // Folders first, then files if (a.type !== b.type) { return a.type === 'folder' ? -1 : 1 } // Then by sort_order, then by name if (a.sort_order !== b.sort_order) { return (a.sort_order || 0) - (b.sort_order || 0) } return a.name.localeCompare(b.name) }) // Recursively sort children items.forEach(item => { if (item.children.length > 0) { sortItems(item.children as (FileSystemItem & { children: FileSystemItem[] })[]) } }) } sortItems(roots) return roots } // GET /api/books/[id]/files - Get file tree for a book export async function GET( request: Request, { params }: { params: { id: string } } ) { try { const supabase = createServerSupabaseClient(request) const bookId = params.id // Get all file system items for this book (RLS will handle access control) const { data: items, error } = await supabase .from('file_system_items') .select('*') .eq('book_id', bookId) .order('sort_order', { ascending: true }) if (error) { console.error('Error fetching file system items:', error) return NextResponse.json( { error: 'Failed to fetch files' }, { status: 500 } ) } // Build tree structure const fileTree = buildFileTree(items || []) return NextResponse.json({ files: fileTree, total: items?.length || 0 }) } catch (error) { console.error('Error in GET /api/books/[id]/files:', error) return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ) } } // POST /api/books/[id]/files - Create a new file or folder export async function POST( request: Request, { params }: { params: { id: string } } ) { try { const supabase = createServerSupabaseClient(request) const bookId = params.id const body: CreateFileSystemItemRequest = await request.json() // Validate required fields if (!body.name || !body.type) { return NextResponse.json( { error: 'Name and type are required' }, { status: 400 } ) } // If parent_id is provided, validate it exists (RLS will handle access control) if (body.parent_id) { const { data: parent, error: parentError } = await supabase .from('file_system_items') .select('id, type') .eq('id', body.parent_id) .eq('book_id', bookId) .single() if (parentError || !parent) { return NextResponse.json( { error: 'Parent folder not found' }, { status: 400 } ) } if (parent.type !== 'folder') { return NextResponse.json( { error: 'Parent must be a folder' }, { status: 400 } ) } } // Get next sort order const { data: existingItems } = await supabase .from('file_system_items') .select('sort_order') .eq('book_id', bookId) .eq('parent_id', body.parent_id || null) .order('sort_order', { ascending: false }) .limit(1) const nextSortOrder = (existingItems?.[0]?.sort_order || 0) + 1 // Prepare item data const itemData: any = { book_id: bookId, parent_id: body.parent_id || null, name: body.name, type: body.type, sort_order: body.sort_order || nextSortOrder } // Add file-specific fields if (body.type === 'file') { itemData.content = body.content || '' itemData.file_extension = body.file_extension || 'md' itemData.mime_type = body.mime_type || 'text/markdown' } else { // For folders itemData.expanded = false } // Create the item const { data: newItem, error } = await supabase .from('file_system_items') .insert(itemData) .select() .single() if (error) { console.error('Error creating file system item:', error) return NextResponse.json( { error: 'Failed to create item' }, { status: 500 } ) } return NextResponse.json({ item: newItem, message: `${body.type === 'file' ? 'File' : 'Folder'} created successfully` }) } catch (error) { console.error('Error in POST /api/books/[id]/files:', error) return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ) } }